package com.guozcc.gateway.nacos.sentinel;

import com.alibaba.csp.sentinel.cluster.ClusterStateManager;
import com.alibaba.csp.sentinel.cluster.client.config.ClusterClientAssignConfig;
import com.alibaba.csp.sentinel.cluster.client.config.ClusterClientConfig;
import com.alibaba.csp.sentinel.cluster.client.config.ClusterClientConfigManager;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Properties;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;

/**
 * @PostConstruct注解，CommandLineRunner和ApplicationRunner
 * 都可以
 */
@Slf4j
@Component
public class LoadSentinelRulesFromNacos implements ApplicationRunner {

    @Value("${sentinel.appname}")
    private String appName;
    @Value("${sentinel.nacos.address}")
    private String serverAddr;
    @Value("${sentinel.nacos.group}")
    private String groupId;
    @Value("${sentinel.nacos.namespace}")
    private String namespace;
    @Value("${sentinel.server.address}")
    private String cluster_server_host;
    @Value("${sentinel.server.port}")
    private String cluster_server_port;
    @Value("${sentinel.mode}")
    private String mode;
    @Override
    public void run(ApplicationArguments args) throws Exception {
        if("2".equals(mode)){//不进行流控
            return;
        }else if("1".equals(mode)){//默认流控模式，集群模式，走集群配置
            this.loadClusterClientConfig();
        }
        log.info("加载nacos流控规则");
        this.registerClusterFlowRuleProperty();
        this.registerClusterParamFlowRuleProperty();
        this.registerClusterDegradeRuleProperty();
        this.registerClusterAuthorityRuleProperty();
        this.registerClusterSystemRuleProperty();
    }

    /*
     * 设置服务端参数
     */
    private void loadClusterClientConfig() {
        ClusterClientAssignConfig assignConfig = new ClusterClientAssignConfig();
        assignConfig.setServerHost(this.cluster_server_host);
        assignConfig.setServerPort(Integer.valueOf(this.cluster_server_port));
        ClusterClientConfigManager.applyNewAssignConfig(assignConfig);

        ClusterClientConfig clientConfig = new ClusterClientConfig();
        clientConfig.setRequestTimeout(Integer.valueOf(20));
        ClusterClientConfigManager.applyNewConfig(clientConfig);
        //指定为client模式
        if (!ClusterStateManager.isClient()) {
            //ClusterStateManager.applyState(Integer.valueOf(0));
            ClusterStateManager.applyState(ClusterStateManager.CLUSTER_CLIENT);
        }
    }
    //注册 -flow-rules 规则监听  流控
    private void registerClusterFlowRuleProperty() {
        Converter<String, List<FlowRule>>  c = source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {});
        ReadableDataSource ds = new NacosDataSource(new Properties(){{
            put("serverAddr", serverAddr);
            put("namespace", namespace);
        }},groupId,appName+"-flow-rules",c);
        FlowRuleManager.register2Property(ds.getProperty());
    }
    //注册 -param-rules 规则监听 热点
    private void registerClusterParamFlowRuleProperty() {
        Converter<String, List<ParamFlowRule>>  c = source -> JSON.parseObject(source, new TypeReference<List<ParamFlowRule>>() {});
        ReadableDataSource ds = new NacosDataSource(new Properties(){{
            put("serverAddr", serverAddr);
            put("namespace", namespace);
        }},groupId,appName+"-param-rules",c);
        ParamFlowRuleManager.register2Property(ds.getProperty());
    }
    //注册 -degrade-rules 规则监听 熔断降级规则   jdk1.7写法
    private void registerClusterDegradeRuleProperty() {
        // 指定Nacos中namespace
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, serverAddr);
        properties.put(PropertyKeyConst.NAMESPACE, namespace);
        Converter<String, List<DegradeRule>>  c = new Converter<String, List<DegradeRule>>() {
            @Override
            public List<DegradeRule> convert(String source) {
                return JSON.parseObject(source, new TypeReference<List<DegradeRule>>() {});
            }
        };
        ReadableDataSource ds = new NacosDataSource(properties, this.groupId,this.appName + "-degrade-rules", c);
        DegradeRuleManager.register2Property(ds.getProperty());
    }
    //注册  -auth-rules 规则监听 授权流控规则   jdk1.7写法
    private void registerClusterAuthorityRuleProperty(){
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, serverAddr);
        properties.put(PropertyKeyConst.NAMESPACE, namespace);
        Converter<String, List<AuthorityRule>>  c = source -> {
            List<JSONObject> list = JSONArray.parseObject(source, List.class);
            List<AuthorityRule> authorityRules = list.stream().map(o -> {
                return JSONObject.parseObject(o.toJSONString(), AuthorityRule.class);
//                        .setStrategy(RuleConstant.AUTHORITY_BLACK);//控制台有BUG,无法修改黑白名单，此处指定一下，只要进行配置，默认都是黑名单
            }).collect(Collectors.toList());
            return authorityRules;
        };
        ReadableDataSource ds = new NacosDataSource(properties, this.groupId,this.appName + "-auth-rules", c);
        AuthorityRuleManager.register2Property(ds.getProperty());
    }
    //注册 -system-rules 规则监听  系统规则
    private void registerClusterSystemRuleProperty() {
        Converter<String, List<SystemRule>>  c = source -> JSON.parseObject(source, new TypeReference<List<SystemRule>>() {});
        ReadableDataSource ds = new NacosDataSource(new Properties(){{
            put("serverAddr", serverAddr);
            put("namespace", namespace);
        }}, this.groupId,this.appName + "-system-rules", c);
        SystemRuleManager.register2Property(ds.getProperty());
    }

































    //手动加载规则示例
    public  void initFlowRules()  {
        final String dataId = appName+"-flow-rules";
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, serverAddr);
        properties.put(PropertyKeyConst.NAMESPACE, namespace);
        try {
            ConfigService configService = NacosFactory.createConfigService(properties);
            configService.addListener(dataId, groupId, new Listener() {
                @Override
                public void receiveConfigInfo(String configInfo) {
                    List<JSONObject> list = JSONArray.parseObject(configInfo, List.class);
                    List<FlowRule> rules = list.stream().map(o -> JSONObject.parseObject(o.toJSONString(),FlowRule.class)).collect(Collectors.toList());
                    FlowRuleManager.loadRules(rules);
                    log.info("规则变更："+rules);
                }
                @Override
                public Executor getExecutor() {
                    return null;
                }
            });
            //首次加载
            String content = configService.getConfig(dataId, groupId, 5000);
            List<JSONObject> list = JSONArray.parseObject(content, List.class);
            List<FlowRule> rules = list.stream().map(o -> JSONObject.parseObject(o.toJSONString(),FlowRule.class)).collect(Collectors.toList());
            FlowRuleManager.loadRules(rules);
        }catch (NacosException e){
            log.error("创建nacos监听异常",e);
        }
    }
}



